package com.twu.biblioteca.inputparser;

import com.twu.biblioteca.commandexecutor.AbstractBibliotecaCommandExecutor;
import com.twu.biblioteca.commandexecutor.BibliotecaCommandExecutor_SubModules;
import com.twu.biblioteca.commandexecutor.NullCommandException;
import com.twu.biblioteca.commandexecutor.ProgramExitRequestedException;
import com.twu.biblioteca.librarycollection.LibraryCollection;
import com.twu.biblioteca.memberdata.MemberData;
import com.twu.biblioteca.memberdata.UserDetails;

import java.io.*;

public class BibliotecaCommandParser {

    private final MemberData memberData;
    private final UserDetails userDetails;
    AbstractBibliotecaCommandExecutor executor;

    InputStream inStream;
    BufferedReader reader;
    OutputStream outStream;
    BufferedWriter writer;

    LibraryCollection libraryColl;

    public static void main(String[] args) throws Exception {
        LibraryCollection libraryColl = new LibraryCollection();
        libraryColl.addBook("BookA");
        libraryColl.addBook("BookB");
        libraryColl.addBook("BookC");

        libraryColl.addMovie("MovieA", "1923", "Joe. Mclamore", 6);
        libraryColl.addMovie("Le Muerte de Bu", "2010", "Hi", 2);
        libraryColl.addMovie("A", "1923", "A", 1);
        libraryColl.addMovie("Naro", "1923", "Guuu", 10);

        MemberData  memberData = new MemberData();
        //Note that the (automatically assigned) user id is 111-1111
        memberData.addMember("damxam@gmail.com", "password");

        BibliotecaCommandParser app = BibliotecaCommandParser.createApp(libraryColl, memberData, System.in, System.out);
        app.run();
    }

    private BibliotecaCommandParser(LibraryCollection libraryColl,MemberData memberData,  AbstractBibliotecaCommandExecutor executor, InputStream inStream, OutputStream outStream)
    {
        this.libraryColl = libraryColl;
        this.memberData = memberData;
        this.userDetails = new UserDetails();
        this.executor = executor;
        this.inStream = inStream;
        this.outStream = outStream;
        reader = new BufferedReader(new InputStreamReader(inStream));
        writer = new BufferedWriter(new OutputStreamWriter(outStream));
    }

    public static BibliotecaCommandParser createStandardIOApp(LibraryCollection libColl, MemberData memberData, UserDetails userDetails)
    {
        return new BibliotecaCommandParser(libColl,  memberData,BibliotecaCommandExecutor_SubModules.createCommandExecutorWithStandardSubExecutors(), System.in, System.out);
    }

    public static BibliotecaCommandParser createApp(LibraryCollection libraryColl, MemberData memberData, InputStream inStream, OutputStream outStream)
    {
        return new BibliotecaCommandParser(
                libraryColl, memberData,
                BibliotecaCommandExecutor_SubModules.createCommandExecutorWithStandardSubExecutors(),
                inStream, outStream);
    }

    public void run()
    {
        printHelloMessage();

        while(true)
        {
            try
            {
                boolean keepGoing = processNextCommand();
                if(!keepGoing)
                {
                    printByeMessage();
                    break;
                }
            }
            catch(IOException ioException)
            {
                handleIOException(ioException);
            }
        }
        closeStreams();
    }

    private void printHelloMessage()
    {
        try
        {
            writer.write(executor.getHelloMessage() + "\n");
            writer.write(executor.getMenu());
            writer.write(executor.getEnterNextCommandMessage());
            writer.flush();
        }
        catch(IOException io)
        {
            io.printStackTrace();
        }
    }

    private void printByeMessage()
    {
        try
        {
            writer.write(executor.getByeMessage() + "\n");
            writer.flush();
        }
        catch(IOException io)
        {
            io.printStackTrace();
        }
    }

    private void handleIOException(IOException ioException)
    {
        try
        {
            writer.write("Error: " + ioException.getMessage());
            writer.flush();
        }
        catch(IOException ioException2)
        {
            ioException2.printStackTrace();
        }
    }

    private boolean processNextCommand() throws IOException
    {
        try
        {
            String command;

            try
            {
                command = getNextCommand();
            }
            catch(NullCommandException nce)
            {
                return true;
            }

            String output = executor.processCommand(libraryColl, memberData, userDetails, command);
            writer.write(output + "\n");

            //writer.write(executor.getEnterNextCommandMessage());
            //writer.write(executor.getMenu());
            writer.flush();
        }
        catch(ProgramExitRequestedException pere)
        {
            return false;
        }
        return true;
    }

    private String getNextCommand() throws NullCommandException, ProgramExitRequestedException, IOException
    {
        String command = reader.readLine();
        testForNullCommandThrowException(command);
        command = cleanCommand(command);
        testForEmptyCommandThrowException(command);
        return command;
    }

    private void testForNullCommandThrowException(String command) throws ProgramExitRequestedException
    {
        if(command == null)
            throw new ProgramExitRequestedException();
    }

    private String cleanCommand(String command)
    {
        command = command.trim();
        return command;
    }

    private void testForEmptyCommandThrowException(String command) throws NullCommandException
    {
        if(command.equals(""))
            throw new NullCommandException();
    }

    private void closeStreams()
    {
        try
        {
            reader.close();
            writer.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
}
